﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class DebugCommandUI :ScFramework.Core.UIBase
{
    //用于存储和快速查找子物体
    private Dictionary<string, GameObject> __gos;

    private Dictionary<string, CmdMethodInfo> _cmdDic = new Dictionary<string, CmdMethodInfo>();

    private readonly object[] NullArgArray = new object[] { };

    private CmdMethodInfo _currentTyreMeth = null;

    //按Tab的次序
    private int _tabIndex = 0;

    private List<string> _history = new List<string>();
    //当前input是被自动编辑功能操作的，比如按Tab
    private bool _isEditByAuto = false;

    //按下table的时候开始找的词语
    private string _tabStart = "";

    //按上下键的次序
    private int _upDownIndex = 0;

    private bool _panelActive = false;

    private class CmdMethodInfo
    {
        public string MethName;
        public string NameText;
        public MethodInfo Method;
        public string[] ArgsText;
    }

    private class CmdFold
    {
        public string Name;
        public CmdFold Parent;
        public List<CmdFold> ChildrenFold;
        public List<CmdMethodInfo> Meths;

        public CmdFold(string name)
        {
            Name = name;
            ChildrenFold = new List<CmdFold>();
            Meths = new List<CmdMethodInfo>();
        }

        public string Path
        {
            get
            {
                StringBuilder sb = new StringBuilder();
                var current = this;
                while (null != current)
                {
                    sb.Insert(0, current.Name);
                    sb.Insert(0, "/");
                    current = current.Parent;
                }
                return sb.ToString();
            }
        }
    }

    private static DebugCommandUI _instance;

    public static DebugCommandUI Instance
    {
        get
        {
            return _instance;
        }
    }

    /// <summary>
    /// 根文件夹
    /// </summary>
    private CmdFold _rootFold = new CmdFold("");

    /// <summary>
    /// 根文件夹
    /// </summary>
    private CmdFold _currentFold = null;

    private void Awake()
    {
#if !DEBUG
        Destroy(gameObject);
        return;
#endif
        if (_instance == null)
        {
            _instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else if (Instance != this)
        {
            Destroy(gameObject);
            return;
        }

        if (!Debug.isDebugBuild)
        {
            Destroy(gameObject);
            return;
        }

        CollectObject();
        CollectCmdType();
        CollectCmdFold();

        Get<Button>("DebugButton").onClick.AddListener(ShowOrHide);
        Get<Button>("OKButton").onClick.AddListener(OnClickRunCmd);
        Get<InputField>("CmdInputField").onValueChanged.AddListener(OnEdit);
        EventTrigger.Entry myclick = new EventTrigger.Entry();
        myclick.eventID = EventTriggerType.PointerDown;
        myclick.callback.AddListener(FastComplete);

        Get<EventTrigger>("ArgHintText").triggers.Add(myclick);

        Get<Button>("ReturnButton").onClick.AddListener(OnClickReturnFold);

        ShowFold(_rootFold);
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.BackQuote))
        {
            ShowOrHide();
        }

        if (!_panelActive)
        {
            return;
        }

        //强行获取焦点
        {
            Get<InputField>("CmdInputField").ActivateInputField();
        }

        if (Input.GetKeyDown(KeyCode.Return))
        {
            OnClickRunCmd();
        }
        if (Input.GetKeyDown(KeyCode.Tab))
        {
            if (_tabIndex == 0)
            {
                _tabStart = Get<InputField>("CmdInputField").text;
            }
            _currentTyreMeth = TryGetInfo(_tabStart, true);
            FastComplete(null);
            _tabIndex++;
        }
        if (Input.GetKeyDown(KeyCode.UpArrow))
        {
            if (_history.Count >0)
            {
                _upDownIndex--;
                string cmd = _history[(_upDownIndex % _history.Count + _history.Count) % _history.Count];
                FastCompleteOrRun(cmd,"", false);
            }
        }
        if (Input.GetKeyDown(KeyCode.DownArrow))
        {
            if (_history.Count > 0)
            {
                _upDownIndex++;
                string cmd = _history[(_upDownIndex % _history.Count + _history.Count) % _history.Count];
                FastCompleteOrRun(cmd, "", false);
            }
        }
    }

    private void ShowFold(CmdFold fold)
    {
        _currentFold = fold;
        Get<Text>("PathText").text = fold.Path;
        int count = fold.ChildrenFold.Count + fold.Meths.Count;
        var parent = Get<RectTransform>("Grid");
        for (int i = parent.childCount; i < count; i++)
        {
            GameObject.Instantiate(parent.GetChild(0), parent);
        }
        for (int i = count; i < parent.childCount; i++)
        {
            parent.GetChild(i).gameObject.SetActive(false);
        }

        for (int i = 0; i < count; i++)
        {
            parent.GetChild(i).gameObject.SetActive(true);
        }

        for (int i = 0; i < fold.ChildrenFold.Count; i++)
        {
            parent.GetChild(i).GetComponent<Button>().onClick.RemoveAllListeners();
            int index = i;
            parent.GetChild(i).GetComponentInChildren<Text>().text = fold.ChildrenFold[index].Name + ">";
            parent.GetChild(i).GetComponent<Button>().onClick.AddListener(
                () =>
                {
                    ShowFold(fold.ChildrenFold[index]);
                }
                );
        }
        for (int i = 0; i < fold.Meths.Count; i++)
        {
            parent.GetChild(i + fold.ChildrenFold.Count).GetComponent<Button>().onClick.RemoveAllListeners();
            int index = i;
            string buttonText = fold.Meths[index].NameText;
            int last = buttonText.LastIndexOf('/');
            if (last > 0)
            {
                buttonText = buttonText.Substring(last + 1);
            }
            parent.GetChild(i + fold.ChildrenFold.Count).GetComponentInChildren<Text>().text = buttonText;
            parent.GetChild(i + fold.ChildrenFold.Count).GetComponent<Button>().onClick.AddListener(
            () =>
            {
                FastCompleteOrRun(fold.Meths[index].MethName);
            }
            );
        }
    }

    private void OnClickReturnFold()
    {
        if (_currentFold != null && _currentFold.Parent != null)
        {
            ShowFold(_currentFold.Parent);
        }
    }

    private void ShowOrHide()
    {
        Get<InputField>("CmdInputField").text = "";
        var panel = Get<RectTransform>("MainPanel").gameObject;
        _panelActive = !_panelActive;
        panel.SetActive(_panelActive);
    }

    private void FastComplete(BaseEventData arg0)
    {
        if (_currentTyreMeth != null)
        {
            _isEditByAuto = true;
            Get<InputField>("CmdInputField").text = _currentTyreMeth.MethName + " ";
            Get<InputField>("CmdInputField").ActivateInputField();
            Get<InputField>("CmdInputField").caretPosition = Get<InputField>("CmdInputField").text.Length;
            _isEditByAuto = false;
        }
    }

    /// <summary>
    /// 快速填充或者运行
    /// </summary>
    /// <param name="cmd"></param>
    private void FastCompleteOrRun(string cmd,string append  = " ",  bool autoRun = true)
    {
        Get<InputField>("CmdInputField").text = cmd + append;
        Get<InputField>("CmdInputField").ActivateInputField();
        Get<InputField>("CmdInputField").caretPosition = Get<InputField>("CmdInputField").text.Length;
        var methInfo = TryGetInfo(cmd);
        if (autoRun && methInfo != null && methInfo.ArgsText.Length == 0)
        {
            OnClickRunCmd();
        }
    }

    private void OnEdit(string cmd)
    {
        //一旦编辑就清掉
        if (!_isEditByAuto)
        {
            _upDownIndex = 0;
            _tabIndex = 0;
        }

        _currentTyreMeth = TryGetInfo(cmd);
        if (_currentTyreMeth != null)
        {
            Get<Text>("HintText").text = _currentTyreMeth.NameText;
            StringBuilder sb = new StringBuilder();
            sb.Append(_currentTyreMeth.MethName);
            sb.Append("  ");
            for (int i = 0; i < _currentTyreMeth.ArgsText.Length; i++)
            {
                sb.Append("[");
                sb.Append(_currentTyreMeth.ArgsText[i]);
                sb.Append("]");
                sb.Append("  ");
            }
            Get<Text>("ArgHintText").text = sb.ToString();
        }
    }

    private void OnClickRunCmd()
    {
        string cmd = Get<InputField>("CmdInputField").text;
        if (string.IsNullOrEmpty(cmd))
        {
            return;
        }
        Get<InputField>("CmdInputField").text = string.Empty;
        _upDownIndex = 0;
        _tabIndex = 0;
        RunCmd(cmd);
    }

    /// <summary>
    /// 粗略预估需要用到的功能
    /// </summary>
    /// <param name="cmd"></param>
    /// <returns></returns>
    private CmdMethodInfo TryGetInfo(string cmd, bool tab = false)
    {
        string[] cmds = cmd.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

        if (cmds.Length == 0)
        {
            return null;
        }

        //转换成小写查找，这样忽略大小写
        string lower = cmds[0].ToLower();
        int currentIndex = _tabIndex;
        if (!_cmdDic.ContainsKey(lower))
        {
            do
            {
                foreach (var pair in _cmdDic)
                {
                    if (pair.Key.Contains(lower))
                    {
                        if (tab && currentIndex > 0)
                        {
                            currentIndex--;
                            continue;
                        }
                        lower = pair.Key;
                        currentIndex = -1;
                        break;
                    }
                }
                //如果当前转完一圈没找全，再继续
            } while (currentIndex >= 0 && _tabIndex != currentIndex);
        }

        //如果循环完还是没找齐想要的
        if (!_cmdDic.ContainsKey(lower))
        {
            return null;
        }

        List<string> argStrList = new List<string>(cmds);
        argStrList.RemoveAt(0);
        var argObjList = new List<object>();

        var methinfo = _cmdDic[lower];
        return methinfo;
    }

    public void RunCmd(string cmd)
    {
        ShowComent("> " + cmd);
        cmd = cmd.Trim();
        _history.Add(cmd);
        string[] cmds = cmd.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

        if (cmds.Length == 0)
        {
            ShowComent("不能为空");
            return;
        }

        if (!_cmdDic.ContainsKey(cmds[0].ToLower()))
        {
            ShowComent("找不到该命令 " + cmds[0]);
            return;
        }
        List<string> argStrList = new List<string>(cmds);
        argStrList.RemoveAt(0);
        var argObjList = new List<object>();
        var info = _cmdDic[cmds[0].ToLower()];
        var methinfo = info.Method;

        if (info.ArgsText.Length != argStrList.Count)
        {
            ShowComent("参数数量不正确");
            return;
        }

        var paramsTypes = methinfo.GetParameters();
        if (paramsTypes.Length == 0)
        {
            string ret = string.Empty;
            try
            {
                 ret = (string) methinfo.Invoke(null, NullArgArray);
            }
            catch (Exception e)
            {
                if (e.InnerException != null)
                {
                    ShowComent(e.InnerException.Message);
                }
                else
                {
                    ShowComent(e.ToString());
                }
                throw;
            }

            ShowComent((string)ret);
        }
        else
        {
            //根据具体的参数类型逐个转换需要的参数
            try
            {
                foreach (var paramType in paramsTypes)
                {
                    int index = argObjList.Count;
                    if (paramType.ParameterType == typeof(int))
                    {
                        argObjList.Add(int.Parse(argStrList[index]));
                    }
                    else if (paramType.ParameterType == typeof(float))
                    {
                        argObjList.Add(float.Parse(argStrList[index]));
                    }else if (paramType.ParameterType == typeof(uint))
                    {
                        argObjList.Add(uint.Parse(argStrList[index]));
                    }
                    else if (paramType.ParameterType == typeof(ushort))
                    {
                        argObjList.Add(ushort.Parse(argStrList[index]));
                    }
                    if (paramType.ParameterType == typeof(string))
                    {
                        argObjList.Add(argStrList[index]);
                    }
                }
                string ret = string.Empty;
                try
                {
                     ret = (string) methinfo.Invoke(null, argObjList.ToArray());
                }
                catch (Exception e)
                {
                    if (e.InnerException != null)
                    {
                        ShowComent(e.InnerException.Message);
                    }
                    else
                    {
                        ShowComent(e.ToString());
                    }
                    throw;
                }
                ShowComent((string)ret);
            }
            catch (FormatException)
            {
                ShowComent("参数格式有误");
            }
        }
    }

    private void ShowComent(string cmd)
    {
        var parent = Get<RectTransform>("CmdContent");
        var t = parent.GetChild(0);

        Transform go = Instantiate(t, parent);
        go.GetComponent<Text>().text = cmd;
        go.GetComponent<ContentSizeFitter>().SetLayoutVertical();
        Get<VerticalLayoutGroup>("CmdContent").CalculateLayoutInputVertical();
        var p = parent.anchoredPosition;
        p.y = parent.sizeDelta.y / 2f + go.GetComponent<RectTransform>().sizeDelta.y;
        parent.anchoredPosition = p;
    }

    /// <summary>
    /// 一个快速获取的函组件数，以物体名为Key，相同值则直接覆盖
    /// </summary>
    /// <typeparam name="T">组件</typeparam>
    /// <param name="name">物体名。</param>
    /// <returns>组件对象</returns>
    /// <exception cref="System.ArgumentNullException">没找到组件。</exception>
    public new T Get<T>(string name)
    {
        if (__gos == null || !__gos.ContainsKey(name))
        {
            CollectObject();
        }
        if (!__gos.ContainsKey(name))
        {
            throw new System.ArgumentNullException(name);
        }

        return __gos[name].GetComponent<T>();
    }

    /// <summary>
    /// 清除字典里的值
    /// </summary>
    public void ClearDictionary()
    {
        __gos.Clear();
    }

    /// <summary>
    /// 建立一个包含所有子物体的字典
    /// </summary>
    public new void CollectObject()
    {
        //获取所有子物体
        __gos = new Dictionary<string, GameObject>();
        var childs = transform.GetComponentsInChildren<Transform>(true);//包括隐藏物体

        foreach (Transform child in childs)
        {
            __gos[child.name] = child.gameObject;
        }
    }

    /// <summary>
    /// 建立一个包含所有子物体的字典
    /// </summary>
    public void CollectCmdType()
    {
        var types = GetType().Assembly.GetTypes();
        for (int i = 0; i < types.Length; i++)
        {
            var t = types[i];
            var ats = t.GetCustomAttributes(true);
            for (int j = 0; j < ats.Length; j++)
            {
                if (ats[j] is DebugCmdTypeAttribute)
                {
                    DebugCmdTypeAttribute cmdType = ats[j] as DebugCmdTypeAttribute;
                    //是个指定的命令类
                    CollectCmdMeth(cmdType.TypeName, t);
                    break;
                }
            }
        }
    }

    /// <summary>
    /// 树状图
    /// </summary>
    public void CollectCmdFold()
    {
        foreach (var pair in _cmdDic)
        {
            var paths = pair.Value.NameText.Split('/');
            var current = _rootFold;
            for (int i = 0; i < paths.Length; i++)
            {
                if (paths.Length - 1 == i)
                {
                    current.Meths.Add(pair.Value);
                }
                else
                {
                    var next = current.ChildrenFold.Find((c) => { return c.Name == paths[i]; });
                    if (next == null)
                    {
                        next = new CmdFold(paths[i]);
                        next.Parent = current;
                        current.ChildrenFold.Add(next);
                    }
                    current = next;
                }
            }
        }
    }

    /// <summary>
    ///  在类型里找命令静态函数
    /// </summary>
    public void CollectCmdMeth(string parent, Type type)
    {
        var ms = type.GetMethods();

        for (int i = 0; i < ms.Length; i++)
        {
            var mts = ms[i].GetCustomAttributes(true);
            for (int j = 0; j < mts.Length; j++)
            {
                if (mts[j] is DebugCmdAttribute)
                {
                    var debugCmdAttribute = mts[j] as DebugCmdAttribute;
                    var info = new CmdMethodInfo();

                    info.NameText = parent + "/" + debugCmdAttribute.FuncName;
                    info.Method = ms[i];
                    info.ArgsText = debugCmdAttribute.Args;
                    info.MethName = ms[i].Name.ToLower();
                    //记录函数
                    _cmdDic[ms[i].Name.ToLower()] = info;
                }
            }
        }
    }
}

public class DebugCmdAttribute : Attribute
{
    private string _funcName;
    private string[] _args;

    public DebugCmdAttribute(string name, params string[] args)
    {
        _funcName = name;
        _args = args;
    }

    public string FuncName
    {
        get { return _funcName; }
    }

    public string[] Args
    {
        get { return _args; }
    }
}

public class DebugCmdTypeAttribute : Attribute
{
    private string _typeName;

    public DebugCmdTypeAttribute(string name)
    {
        _typeName = name;
    }

    public string TypeName
    {
        get { return _typeName; }
    }
}